/**
11/14/2011 start and write a monte carlo pricer
11/15/2011 define PayOff interface, bridge to European Option with bridge pattern  

To do:
recombine MC products and Tree products
results(MCstatistics)
random number generator interface;
*/

#include <iostream>
#include <boost\program_options.hpp>
namespace po = boost::program_options;
#include <boost\foreach.hpp>

#include "EuropeanOption.h"
#include "VanillaPayOff.h"
#include "PayOffBridge.h"
#include "SimpleMC.h"
#include "PayOffFactory.h"
#include "ParameterConstant.h"

#include "BinomialTree.h"
#include "EuropeanProduct.h"
#include "AmericanProduct.h"


int main(int argc, char *argv[])
{
	double spot,strike,r,vol,expiry;
	int paths,steps;

	po::options_description desc("Allow options");
	desc.add_options()
		("help", "help message")
		("s",po::value<double>(&spot)->default_value(90),"spot asset price")
		("k",po::value<double>(&strike)->default_value(100),"strike price")
		("r",po::value<double>(&r)->default_value(0.05),"interest rate")
		("vol",po::value<double>(&vol)->default_value(0.3),"volatility")
		("expiry",po::value<double>(&expiry)->default_value(1),"time to maturity")
		("paths",po::value<int>(&paths)->default_value(100),"number of paths")
		("steps",po::value<int>(&steps)->default_value(100),"number of steps")
	;	
	po::variables_map vm;
	po::store(po::parse_command_line(argc,argv,desc),vm);
	po::notify(vm);

	if(vm.count("help")) {
		std::cout << desc << std::endl;
		return 1;
	}
	std::cout << "Strike " << strike << std::endl;
	std::cout << "Spot   " << spot << std::endl;
	std::cout << "interest rate " << r << std::endl;
	std::cout << "volatility    " << vol << std::endl;
	std::cout << "expiry " << expiry << std::endl;
	std::cout << "paths " << paths << std::endl;
	std::cout << "steps " << steps << std::endl;

	const ParameterConstant constant_r(r);
	const ParameterConstant constant_vol(vol);
	std::vector<std::string> payoffs_id = PayOffFactory::Instance().getAllPayOffs();

//Monte Carlo
	std::cout << "<<< Monte Carlo >>>" << std::endl;
	MonteCarlo simpleMC(spot,constant_r,constant_vol);

	for(std::vector<std::string>::const_iterator iter= payoffs_id.begin(); iter!=payoffs_id.end(); ++iter) {
		PayOff* payoff_ptr = PayOffFactory::Instance().createPayOff(*iter,strike);
		EuropeanProduct product(*payoff_ptr,expiry);
		double price = simpleMC.calcPrice(paths,product);
		std::cout << *iter << ": " << price << std::endl;
	}

//Tree
	std::cout << "<<< Tree >>>" << std::endl;
	BinomialTree binomial_tree(spot,constant_r,constant_vol,expiry,steps);
	BOOST_FOREACH(std::string payoff_id,payoffs_id)
	{
		PayOff* payoff_ptr = PayOffFactory::Instance().createPayOff(payoff_id,strike);
		EuropeanProduct product(*payoff_ptr,expiry);
		double price = binomial_tree.calcPrice(product);
		std::cout << "European"<<payoff_id << ": " << price << std::endl;
	}
	BOOST_FOREACH(std::string payoff_id,payoffs_id)
	{
		PayOff* payoff_ptr = PayOffFactory::Instance().createPayOff(payoff_id,strike);
		AmericanProduct product(*payoff_ptr,expiry);
		double price = binomial_tree.calcPrice(product);
		std::cout << "American"<< payoff_id << ": " << price << std::endl;
	}
	return 0;
}